home *** CD-ROM | disk | FTP | other *** search
/ Ultra Pack / UltraComputing Partner Applications.iso / SunLabs / tclTK / src / tk4.0 / tkOption.c < prev    next >
C/C++ Source or Header  |  1995-06-25  |  39KB  |  1,385 lines

  1. /* 
  2.  * tkOption.c --
  3.  *
  4.  *    This module contains procedures to manage the option
  5.  *    database, which allows various strings to be associated
  6.  *    with windows either by name or by class or both.
  7.  *
  8.  * Copyright (c) 1990-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  */
  14.  
  15. static char sccsid[] = "@(#) tkOption.c 1.41 95/06/25 15:30:42";
  16.  
  17. #include "tkPort.h"
  18. #include "tkInt.h"
  19.  
  20. /*
  21.  * The option database is stored as one tree for each main window.
  22.  * Each name or class field in an option is associated with a node or
  23.  * leaf of the tree.  For example, the options "x.y.z" and "x.y*a"
  24.  * each correspond to three nodes in the tree;  they share the nodes
  25.  * "x" and "x.y", but have different leaf nodes.  One of the following
  26.  * structures exists for each node or leaf in the option tree.  It is
  27.  * actually stored as part of the parent node, and describes a particular
  28.  * child of the parent.
  29.  */
  30.  
  31. typedef struct Element {
  32.     Tk_Uid nameUid;            /* Name or class from one element of
  33.                      * an option spec. */
  34.     union {
  35.     struct ElArray *arrayPtr;    /* If this is an intermediate node,
  36.                      * a pointer to a structure describing
  37.                      * the remaining elements of all
  38.                      * options whose prefixes are the
  39.                      * same up through this element. */
  40.     Tk_Uid valueUid;        /* For leaf nodes, this is the string
  41.                      * value of the option. */
  42.     } child;
  43.     int priority;            /* Used to select among matching
  44.                      * options.  Includes both the
  45.                      * priority level and a serial #.
  46.                      * Greater value means higher
  47.                      * priority.  Irrelevant except in
  48.                      * leaf nodes. */
  49.     int flags;                /* OR-ed combination of bits.  See
  50.                      * below for values. */
  51. } Element;
  52.  
  53. /*
  54.  * Flags in Element structures:
  55.  *
  56.  * CLASS -        Non-zero means this element refers to a class,
  57.  *            Zero means this element refers to a name.
  58.  * NODE -        Zero means this is a leaf element (the child
  59.  *            field is a value, not a pointer to another node).
  60.  *            One means this is a node element.
  61.  * WILDCARD -        Non-zero means this there was a star in the
  62.  *            original specification just before this element.
  63.  *            Zero means there was a dot.
  64.  */
  65.  
  66. #define TYPE_MASK        0x7
  67.  
  68. #define CLASS            0x1
  69. #define NODE            0x2
  70. #define WILDCARD        0x4
  71.  
  72. #define EXACT_LEAF_NAME        0x0
  73. #define EXACT_LEAF_CLASS    0x1
  74. #define EXACT_NODE_NAME        0x2
  75. #define EXACT_NODE_CLASS    0x3
  76. #define WILDCARD_LEAF_NAME    0x4
  77. #define WILDCARD_LEAF_CLASS    0x5
  78. #define WILDCARD_NODE_NAME    0x6
  79. #define WILDCARD_NODE_CLASS    0x7
  80.  
  81. /*
  82.  * The following structure is used to manage a dynamic array of
  83.  * Elements.  These structures are used for two purposes:  to store
  84.  * the contents of a node in the option tree, and for the option
  85.  * stacks described below.
  86.  */
  87.  
  88. typedef struct ElArray {
  89.     int arraySize;        /* Number of elements actually
  90.                  * allocated in the "els" array. */
  91.     int numUsed;        /* Number of elements currently in
  92.                  * use out of els. */
  93.     Element *nextToUse;        /* Pointer to &els[numUsed]. */
  94.     Element els[1];        /* Array of structures describing
  95.                  * children of this node.  The
  96.                  * array will actually contain enough
  97.                  * elements for all of the children
  98.                  * (and even a few extras, perhaps).
  99.                  * This must be the last field in
  100.                  * the structure. */
  101. } ElArray;
  102.  
  103. #define EL_ARRAY_SIZE(numEls) ((unsigned) (sizeof(ElArray) \
  104.     + ((numEls)-1)*sizeof(Element)))
  105. #define INITIAL_SIZE 5
  106.  
  107. /*
  108.  * In addition to the option tree, which is a relatively static structure,
  109.  * there are eight additional structures called "stacks", which are used
  110.  * to speed up queries into the option database.  The stack structures
  111.  * are designed for the situation where an individual widget makes repeated
  112.  * requests for its particular options.  The requests differ only in
  113.  * their last name/class, so during the first request we extract all
  114.  * the options pertaining to the particular widget and save them in a
  115.  * stack-like cache;  subsequent requests for the same widget can search
  116.  * the cache relatively quickly.  In fact, the cache is a hierarchical
  117.  * one, storing a list of relevant options for this widget and all of
  118.  * its ancestors up to the application root;  hence the name "stack".
  119.  *
  120.  * Each of the eight stacks consists of an array of Elements, ordered in
  121.  * terms of levels in the window hierarchy.  All the elements relevant
  122.  * for the top-level widget appear first in the array, followed by all
  123.  * those from the next-level widget on the path to the current widget,
  124.  * etc. down to those for the current widget.
  125.  *
  126.  * Cached information is divided into eight stacks according to the
  127.  * CLASS, NODE, and WILDCARD flags.  Leaf and non-leaf information is
  128.  * kept separate to speed up individual probes (non-leaf information is
  129.  * only relevant when building the stacks, but isn't relevant when
  130.  * making probes;  similarly, only non-leaf information is relevant
  131.  * when the stacks are being extended to the next widget down in the
  132.  * widget hierarchy).  Wildcard elements are handled separately from
  133.  * "exact" elements because once they appear at a particular level in
  134.  * the stack they remain active for all deeper levels;  exact elements
  135.  * are only relevant at a particular level.  For example, when searching
  136.  * for options relevant in a particular window, the entire wildcard
  137.  * stacks get checked, but only the portions of the exact stacks that
  138.  * pertain to the window's parent.  Lastly, name and class stacks are
  139.  * kept separate because different search keys are used when searching
  140.  * them;  keeping them separate speeds up the searches.
  141.  */
  142.  
  143. #define NUM_STACKS 8
  144. static ElArray *stacks[NUM_STACKS];
  145. static TkWindow *cachedWindow = NULL;    /* Lowest-level window currently
  146.                      * loaded in stacks at present. 
  147.                      * NULL means stacks have never
  148.                      * been used, or have been
  149.                      * invalidated because of a change
  150.                      * to the database. */
  151.  
  152. /*
  153.  * One of the following structures is used to keep track of each
  154.  * level in the stacks.
  155.  */
  156.  
  157. typedef struct StackLevel {
  158.     TkWindow *winPtr;        /* Window corresponding to this stack
  159.                  * level. */
  160.     int bases[NUM_STACKS];    /* For each stack, index of first
  161.                  * element on stack corresponding to
  162.                  * this level (used to restore "numUsed"
  163.                  * fields when popping out of a level. */
  164. } StackLevel;
  165.  
  166. /*
  167.  * Information about all of the stack levels that are currently
  168.  * active.  This array grows dynamically to become as large as needed.
  169.  */
  170.  
  171. static StackLevel *levels = NULL;
  172.                 /* Array describing current stack. */
  173. static int numLevels = 0;    /* Total space allocated. */
  174. static int curLevel = -1;    /* Highest level currently in use.  Note:
  175.                  * curLevel is never 0!  (I don't remember
  176.                  * why anymore...) */
  177.  
  178. /*
  179.  * The variable below is a serial number for all options entered into
  180.  * the database so far.  It increments on each addition to the option
  181.  * database.  It is used in computing option priorities, so that the
  182.  * most recent entry wins when choosing between options at the same
  183.  * priority level.
  184.  */
  185.  
  186. static int serial = 0;
  187.  
  188. /*
  189.  * Special "no match" Element to use as default for searches.
  190.  */
  191.  
  192. static Element defaultMatch;
  193.  
  194. /*
  195.  * Forward declarations for procedures defined in this file:
  196.  */
  197.  
  198. static int        AddFromString _ANSI_ARGS_((Tcl_Interp *interp,
  199.                 Tk_Window tkwin, char *string, int priority));
  200. static void        ClearOptionTree _ANSI_ARGS_((ElArray *arrayPtr));
  201. static ElArray *    ExtendArray _ANSI_ARGS_((ElArray *arrayPtr,
  202.                 Element *elPtr));
  203. static void        ExtendStacks _ANSI_ARGS_((ElArray *arrayPtr,
  204.                 int leaf));
  205. static int        GetDefaultOptions _ANSI_ARGS_((Tcl_Interp *interp,
  206.                 TkWindow *winPtr));    
  207. static ElArray *    NewArray _ANSI_ARGS_((int numEls));    
  208. static void        OptionInit _ANSI_ARGS_((TkMainInfo *mainPtr));
  209. static int        ParsePriority _ANSI_ARGS_((Tcl_Interp *interp,
  210.                 char *string));
  211. static int        ReadOptionFile _ANSI_ARGS_((Tcl_Interp *interp,
  212.                 Tk_Window tkwin, char *fileName, int priority));
  213. static void        SetupStacks _ANSI_ARGS_((TkWindow *winPtr, int leaf));
  214.  
  215. /*
  216.  *--------------------------------------------------------------
  217.  *
  218.  * Tk_AddOption --
  219.  *
  220.  *    Add a new option to the option database.
  221.  *
  222.  * Results:
  223.  *    None.
  224.  *
  225.  * Side effects:
  226.  *    Information is added to the option database.
  227.  *
  228.  *--------------------------------------------------------------
  229.  */
  230.  
  231. void
  232. Tk_AddOption(tkwin, name, value, priority)
  233.     Tk_Window tkwin;        /* Window token;  option will be associated
  234.                  * with main window for this window. */
  235.     char *name;            /* Multi-element name of option. */
  236.     char *value;        /* String value for option. */
  237.     int priority;        /* Overall priority level to use for
  238.                  * this option, such as TK_USER_DEFAULT_PRIO
  239.                  * or TK_INTERACTIVE_PRIO.  Must be between
  240.                  * 0 and TK_MAX_PRIO. */
  241. {
  242.     TkWindow *winPtr = ((TkWindow *) tkwin)->mainPtr->winPtr;
  243.     register ElArray **arrayPtrPtr;
  244.     register Element *elPtr;
  245.     Element newEl;
  246.     register char *p;
  247.     char *field;
  248.     int count, firstField, length;
  249. #define TMP_SIZE 100
  250.     char tmp[TMP_SIZE+1];
  251.  
  252.     if (winPtr->mainPtr->optionRootPtr == NULL) {
  253.     OptionInit(winPtr->mainPtr);
  254.     }
  255.     cachedWindow = NULL;    /* Invalidate the cache. */
  256.  
  257.     /*
  258.      * Compute the priority for the new element, including both the
  259.      * overall level and the serial number (to disambiguate with the
  260.      * level).
  261.      */
  262.  
  263.     if (priority < 0) {
  264.     priority = 0;
  265.     } else if (priority > TK_MAX_PRIO) {
  266.     priority = TK_MAX_PRIO;
  267.     }
  268.     newEl.priority = (priority << 24) + serial;
  269.     serial++;
  270.  
  271.     /*
  272.      * Parse the option one field at a time.
  273.      */
  274.  
  275.     arrayPtrPtr = &(((TkWindow *) tkwin)->mainPtr->optionRootPtr);
  276.     p = name;
  277.     for (firstField = 1; ; firstField = 0) {
  278.  
  279.     /*
  280.      * Scan the next field from the name and convert it to a Tk_Uid.
  281.      * Must copy the field before calling Tk_Uid, so that a terminating
  282.      * NULL may be added without modifying the source string.
  283.      */
  284.  
  285.     if (*p == '*') {
  286.         newEl.flags = WILDCARD;
  287.         p++;
  288.     } else {
  289.         newEl.flags = 0;
  290.     }
  291.     field = p;
  292.     while ((*p != 0) && (*p != '.') && (*p != '*')) {
  293.         p++;
  294.     }
  295.     length = p - field;
  296.     if (length > TMP_SIZE) {
  297.         length = TMP_SIZE;
  298.     }
  299.     strncpy(tmp, field, (size_t) length);
  300.     tmp[length] = 0;
  301.     newEl.nameUid = Tk_GetUid(tmp);
  302.     if (isupper(UCHAR(*field))) {
  303.         newEl.flags |= CLASS;
  304.     }
  305.  
  306.     if (*p != 0) {
  307.  
  308.         /*
  309.          * New element will be a node.  If this option can't possibly
  310.          * apply to this main window, then just skip it.  Otherwise,
  311.          * add it to the parent, if it isn't already there, and descend
  312.          * into it.
  313.          */
  314.  
  315.         newEl.flags |= NODE;
  316.         if (firstField && !(newEl.flags & WILDCARD)
  317.             && (newEl.nameUid != winPtr->nameUid)
  318.             && (newEl.nameUid != winPtr->classUid)) {
  319.         return;
  320.         }
  321.         for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
  322.             ; elPtr++, count--) {
  323.         if (count == 0) {
  324.             newEl.child.arrayPtr = NewArray(5);
  325.             *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
  326.             arrayPtrPtr = &((*arrayPtrPtr)->nextToUse[-1].child.arrayPtr);
  327.             break;
  328.         }
  329.         if ((elPtr->nameUid == newEl.nameUid)
  330.             && (elPtr->flags == newEl.flags)) {
  331.             arrayPtrPtr = &(elPtr->child.arrayPtr);
  332.             break;
  333.         }
  334.         }
  335.         if (*p == '.') {
  336.         p++;
  337.         }
  338.     } else {
  339.  
  340.         /*
  341.          * New element is a leaf.  Add it to the parent, if it isn't
  342.          * already there.  If it exists already, keep whichever value
  343.          * has highest priority.
  344.          */
  345.  
  346.         newEl.child.valueUid = Tk_GetUid(value);
  347.         for (elPtr = (*arrayPtrPtr)->els, count = (*arrayPtrPtr)->numUsed;
  348.             ; elPtr++, count--) {
  349.         if (count == 0) {
  350.             *arrayPtrPtr = ExtendArray(*arrayPtrPtr, &newEl);
  351.             return;
  352.         }
  353.         if ((elPtr->nameUid == newEl.nameUid)
  354.             && (elPtr->flags == newEl.flags)) {
  355.             if (elPtr->priority < newEl.priority) {
  356.             elPtr->priority = newEl.priority;
  357.             elPtr->child.valueUid = newEl.child.valueUid;
  358.             }
  359.             return;
  360.         }
  361.         }
  362.     }
  363.     }
  364. }
  365.  
  366. /*
  367.  *--------------------------------------------------------------
  368.  *
  369.  * Tk_GetOption --
  370.  *
  371.  *    Retrieve an option from the option database.
  372.  *
  373.  * Results:
  374.  *    The return value is the value specified in the option
  375.  *    database for the given name and class on the given
  376.  *    window.  If there is nothing specified in the database
  377.  *    for that option, then NULL is returned.
  378.  *
  379.  * Side effects:
  380.  *    The internal caches used to speed up option mapping
  381.  *    may be modified, if this tkwin is different from the
  382.  *    last tkwin used for option retrieval.
  383.  *
  384.  *--------------------------------------------------------------
  385.  */
  386.  
  387. Tk_Uid
  388. Tk_GetOption(tkwin, name, className)
  389.     Tk_Window tkwin;        /* Token for window that option is
  390.                  * associated with. */
  391.     char *name;            /* Name of option. */
  392.     char *className;        /* Class of option.  NULL means there
  393.                  * is no class for this option:  just
  394.                  * check for name. */
  395. {
  396.     Tk_Uid nameId, classId;
  397.     register Element *elPtr, *bestPtr;
  398.     register int count;
  399.  
  400.     /*
  401.      * Note:  no need to call OptionInit here:  it will be done by
  402.      * the SetupStacks call below (squeeze out those nanoseconds).
  403.      */
  404.  
  405.     if (tkwin != (Tk_Window) cachedWindow) {
  406.     SetupStacks((TkWindow *) tkwin, 1);
  407.     }
  408.  
  409.     nameId = Tk_GetUid(name);
  410.     bestPtr = &defaultMatch;
  411.     for (elPtr = stacks[EXACT_LEAF_NAME]->els,
  412.         count = stacks[EXACT_LEAF_NAME]->numUsed; count > 0;
  413.         elPtr++, count--) {
  414.     if ((elPtr->nameUid == nameId)
  415.         && (elPtr->priority > bestPtr->priority)) {
  416.         bestPtr = elPtr;
  417.     }
  418.     }
  419.     for (elPtr = stacks[WILDCARD_LEAF_NAME]->els,
  420.         count = stacks[WILDCARD_LEAF_NAME]->numUsed; count > 0;
  421.         elPtr++, count--) {
  422.     if ((elPtr->nameUid == nameId)
  423.         && (elPtr->priority > bestPtr->priority)) {
  424.         bestPtr = elPtr;
  425.     }
  426.     }
  427.     if (className != NULL) {
  428.     classId = Tk_GetUid(className);
  429.     for (elPtr = stacks[EXACT_LEAF_CLASS]->els,
  430.         count = stacks[EXACT_LEAF_CLASS]->numUsed; count > 0;
  431.         elPtr++, count--) {
  432.         if ((elPtr->nameUid == classId)
  433.             && (elPtr->priority > bestPtr->priority)) {
  434.         bestPtr = elPtr;
  435.         }
  436.     }
  437.     for (elPtr = stacks[WILDCARD_LEAF_CLASS]->els,
  438.         count = stacks[WILDCARD_LEAF_CLASS]->numUsed; count > 0;
  439.         elPtr++, count--) {
  440.         if ((elPtr->nameUid == classId)
  441.             && (elPtr->priority > bestPtr->priority)) {
  442.         bestPtr = elPtr;
  443.         }
  444.     }
  445.     }
  446.     return bestPtr->child.valueUid;
  447. }
  448.  
  449. /*
  450.  *--------------------------------------------------------------
  451.  *
  452.  * Tk_OptionCmd --
  453.  *
  454.  *    This procedure is invoked to process the "option" Tcl command.
  455.  *    See the user documentation for details on what it does.
  456.  *
  457.  * Results:
  458.  *    A standard Tcl result.
  459.  *
  460.  * Side effects:
  461.  *    See the user documentation.
  462.  *
  463.  *--------------------------------------------------------------
  464.  */
  465.  
  466. int
  467. Tk_OptionCmd(clientData, interp, argc, argv)
  468.     ClientData clientData;    /* Main window associated with
  469.                  * interpreter. */
  470.     Tcl_Interp *interp;        /* Current interpreter. */
  471.     int argc;            /* Number of arguments. */
  472.     char **argv;        /* Argument strings. */
  473. {
  474.     Tk_Window tkwin = (Tk_Window) clientData;
  475.     size_t length;
  476.     char c;
  477.  
  478.     if (argc < 2) {
  479.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  480.         " cmd arg ?arg ...?\"", (char *) NULL);
  481.     return TCL_ERROR;
  482.     }
  483.     c = argv[1][0];
  484.     length = strlen(argv[1]);
  485.     if ((c == 'a') && (strncmp(argv[1], "add", length) == 0)) {
  486.     int priority;
  487.  
  488.     if ((argc != 4) && (argc != 5)) {
  489.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  490.             argv[0], " add pattern value ?priority?\"", (char *) NULL);
  491.         return TCL_ERROR;
  492.     }
  493.     if (argc == 4) {
  494.         priority = TK_INTERACTIVE_PRIO;
  495.     } else {
  496.         priority = ParsePriority(interp, argv[4]);
  497.         if (priority < 0) {
  498.         return TCL_ERROR;
  499.         }
  500.     }
  501.     Tk_AddOption(tkwin, argv[2], argv[3], priority);
  502.     return TCL_OK;
  503.     } else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) {
  504.     TkMainInfo *mainPtr;
  505.  
  506.     if (argc != 2) {
  507.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  508.             argv[0], " clear\"", (char *) NULL);
  509.         return TCL_ERROR;
  510.     }
  511.     mainPtr = ((TkWindow *) tkwin)->mainPtr;
  512.     if (mainPtr->optionRootPtr != NULL) {
  513.         ClearOptionTree(mainPtr->optionRootPtr);
  514.         mainPtr->optionRootPtr = NULL;
  515.     }
  516.     cachedWindow = NULL;
  517.     return TCL_OK;
  518.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  519.     Tk_Window window;
  520.     Tk_Uid value;
  521.  
  522.     if (argc != 5) {
  523.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  524.             argv[0], " get window name class\"", (char *) NULL);
  525.         return TCL_ERROR;
  526.     }
  527.     window = Tk_NameToWindow(interp, argv[2], tkwin);
  528.     if (window == NULL) {
  529.         return TCL_ERROR;
  530.     }
  531.     value = Tk_GetOption(window, argv[3], argv[4]);
  532.     if (value != NULL) {
  533.         interp->result = value;
  534.     }
  535.     return TCL_OK;
  536.     } else if ((c == 'r') && (strncmp(argv[1], "readfile", length) == 0)) {
  537.     int priority;
  538.  
  539.     if ((argc != 3) && (argc != 4)) {
  540.         Tcl_AppendResult(interp, "wrong # args:  should be \"",
  541.             argv[0], " readfile fileName ?priority?\"",
  542.             (char *) NULL);
  543.         return TCL_ERROR;
  544.     }
  545.     if (argc == 4) {
  546.         priority = ParsePriority(interp, argv[3]);
  547.         if (priority < 0) {
  548.         return TCL_ERROR;
  549.         }
  550.     } else {
  551.         priority = TK_INTERACTIVE_PRIO;
  552.     }
  553.     return ReadOptionFile(interp, tkwin, argv[2], priority);
  554.     } else {
  555.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  556.         "\": must be add, clear, get, or readfile", (char *) NULL);
  557.     return TCL_ERROR;
  558.     }
  559. }
  560.  
  561. /*
  562.  *--------------------------------------------------------------
  563.  *
  564.  * TkOptionDeadWindow --
  565.  *
  566.  *    This procedure is called whenever a window is deleted.
  567.  *    It cleans up any option-related stuff associated with
  568.  *    the window.
  569.  *
  570.  * Results:
  571.  *    None.
  572.  *
  573.  * Side effects:
  574.  *    Option-related resources are freed.  See code below
  575.  *    for details.
  576.  *
  577.  *--------------------------------------------------------------
  578.  */
  579.  
  580. void
  581. TkOptionDeadWindow(winPtr)
  582.     register TkWindow *winPtr;        /* Window to be cleaned up. */
  583. {
  584.     /*
  585.      * If this window is in the option stacks, then clear the stacks.
  586.      */
  587.  
  588.     if (winPtr->optionLevel != -1) {
  589.     int i;
  590.  
  591.     for (i = 1; i <= curLevel; i++) {
  592.         levels[i].winPtr->optionLevel = -1;
  593.     }
  594.     curLevel = -1;
  595.     cachedWindow = NULL;
  596.     }
  597.  
  598.     /*
  599.      * If this window was a main window, then delete its option
  600.      * database.
  601.      */
  602.  
  603.     if ((winPtr->mainPtr->winPtr == winPtr)
  604.         && (winPtr->mainPtr->optionRootPtr != NULL)) {
  605.     ClearOptionTree(winPtr->mainPtr->optionRootPtr);
  606.     winPtr->mainPtr->optionRootPtr = NULL;
  607.     }
  608. }
  609.  
  610. /*
  611.  *----------------------------------------------------------------------
  612.  *
  613.  * TkOptionClassChanged --
  614.  *
  615.  *    This procedure is invoked when a window's class changes.  If
  616.  *    the window is on the option cache, this procedure flushes
  617.  *    any information for the window, since the new class could change
  618.  *    what is relevant.
  619.  *
  620.  * Results:
  621.  *    None.
  622.  *
  623.  * Side effects:
  624.  *    The option cache may be flushed in part or in whole.
  625.  *
  626.  *----------------------------------------------------------------------
  627.  */
  628.  
  629. void
  630. TkOptionClassChanged(winPtr)
  631.     TkWindow *winPtr;            /* Window whose class changed. */
  632. {
  633.     int i, j, *basePtr;
  634.     ElArray *arrayPtr;
  635.  
  636.     if (winPtr->optionLevel == -1) {
  637.     return;
  638.     }
  639.  
  640.     /*
  641.      * Find the lowest stack level that refers to this window, then
  642.      * flush all of the levels above the matching one.
  643.      */
  644.  
  645.     for (i = 1; i <= curLevel; i++) {
  646.     if (levels[i].winPtr == winPtr) {
  647.         for (j = i; j <= curLevel; j++) {
  648.         levels[j].winPtr->optionLevel = -1;
  649.         }
  650.         curLevel = i-1;
  651.         basePtr = levels[i].bases;
  652.         for (j = 0; j < NUM_STACKS; j++) {
  653.         arrayPtr = stacks[j];
  654.         arrayPtr->numUsed = basePtr[j];
  655.         arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
  656.         }
  657.         if (curLevel <= 0) {
  658.         cachedWindow = NULL;
  659.         } else {
  660.         cachedWindow = levels[curLevel].winPtr;
  661.         }
  662.         break;
  663.     }
  664.     }
  665. }
  666.  
  667. /*
  668.  *----------------------------------------------------------------------
  669.  *
  670.  * ParsePriority --
  671.  *
  672.  *    Parse a string priority value.
  673.  *
  674.  * Results:
  675.  *    The return value is the integer priority level corresponding
  676.  *    to string, or -1 if string doesn't point to a valid priority level.
  677.  *    In this case, an error message is left in interp->result.
  678.  *
  679.  * Side effects:
  680.  *    None.
  681.  *
  682.  *----------------------------------------------------------------------
  683.  */
  684.  
  685. static int
  686. ParsePriority(interp, string)
  687.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  688.     char *string;        /* Describes a priority level, either
  689.                  * symbolically or numerically. */
  690. {
  691.     int priority, c;
  692.     size_t length;
  693.  
  694.     c = string[0];
  695.     length = strlen(string);
  696.     if ((c == 'w')
  697.         && (strncmp(string, "widgetDefault", length) == 0)) {
  698.     return TK_WIDGET_DEFAULT_PRIO;
  699.     } else if ((c == 's')
  700.         && (strncmp(string, "startupFile", length) == 0)) {
  701.     return TK_STARTUP_FILE_PRIO;
  702.     } else if ((c == 'u')
  703.         && (strncmp(string, "userDefault", length) == 0)) {
  704.     return TK_USER_DEFAULT_PRIO;
  705.     } else if ((c == 'i')
  706.         && (strncmp(string, "interactive", length) == 0)) {
  707.     return TK_INTERACTIVE_PRIO;
  708.     } else {
  709.     char *end;
  710.  
  711.     priority = strtoul(string, &end, 0);
  712.     if ((end == string) || (*end != 0) || (priority < 0)
  713.         || (priority > 100)) {
  714.         Tcl_AppendResult(interp,  "bad priority level \"", string,
  715.             "\": must be widgetDefault, startupFile, userDefault, ",
  716.             "interactive, or a number between 0 and 100",
  717.             (char *) NULL);
  718.         return -1;
  719.     }
  720.     }
  721.     return priority;
  722. }
  723.  
  724. /*
  725.  *----------------------------------------------------------------------
  726.  *
  727.  * AddFromString --
  728.  *
  729.  *    Given a string containing lines in the standard format for
  730.  *    X resources (see other documentation for details on what this
  731.  *    is), parse the resource specifications and enter them as options
  732.  *    for tkwin's main window.
  733.  *
  734.  * Results:
  735.  *    The return value is a standard Tcl return code.  In the case of
  736.  *    an error in parsing string, TCL_ERROR will be returned and an
  737.  *    error message will be left in interp->result.  The memory at
  738.  *    string is totally trashed by this procedure.  If you care about
  739.  *    its contents, make a copy before calling here.
  740.  *
  741.  * Side effects:
  742.  *    None.
  743.  *
  744.  *----------------------------------------------------------------------
  745.  */
  746.  
  747. static int
  748. AddFromString(interp, tkwin, string, priority)
  749.     Tcl_Interp *interp;        /* Interpreter to use for reporting results. */
  750.     Tk_Window tkwin;        /* Token for window:  options are entered
  751.                  * for this window's main window. */
  752.     char *string;        /* String containing option specifiers. */
  753.     int priority;        /* Priority level to use for options in
  754.                  * this string, such as TK_USER_DEFAULT_PRIO
  755.                  * or TK_INTERACTIVE_PRIO.  Must be between
  756.                  * 0 and TK_MAX_PRIO. */
  757. {
  758.     register char *src, *dst;
  759.     char *name, *value;
  760.     int lineNum;
  761.  
  762.     src = string;
  763.     lineNum = 1;
  764.     while (1) {
  765.  
  766.     /*
  767.      * Skip leading white space and empty lines and comment lines, and
  768.      * check for the end of the spec.
  769.      */
  770.  
  771.     while ((*src == ' ') || (*src == '\t')) {
  772.         src++;
  773.     }
  774.     if ((*src == '#') || (*src == '!')) {
  775.         do {
  776.         src++;
  777.         if ((src[0] == '\\') && (src[1] == '\n')) {
  778.             src += 2;
  779.             lineNum++;
  780.         }
  781.         } while ((*src != '\n') && (*src != 0));
  782.     }
  783.     if (*src == '\n') {
  784.         src++;
  785.         lineNum++;
  786.         continue;
  787.     } 
  788.     if (*src == '\0') {
  789.         break;
  790.     }
  791.  
  792.     /*
  793.      * Parse off the option name, collapsing out backslash-newline
  794.      * sequences of course.
  795.      */
  796.  
  797.     dst = name = src;
  798.     while (*src != ':') {
  799.         if ((*src == '\0') || (*src == '\n')) {
  800.         sprintf(interp->result, "missing colon on line %d",
  801.             lineNum);
  802.         return TCL_ERROR;
  803.         }
  804.         if ((src[0] == '\\') && (src[1] == '\n')) {
  805.         src += 2;
  806.         lineNum++;
  807.         } else {
  808.         *dst = *src;
  809.         dst++;
  810.         src++;
  811.         }
  812.     }
  813.  
  814.     /*
  815.      * Eliminate trailing white space on the name, and null-terminate
  816.      * it.
  817.      */
  818.  
  819.     while ((dst != name) && ((dst[-1] == ' ') || (dst[-1] == '\t'))) {
  820.         dst--;
  821.     }
  822.     *dst = '\0';
  823.  
  824.     /*
  825.      * Skip white space between the name and the value.
  826.      */
  827.  
  828.     src++;
  829.     while ((*src == ' ') || (*src == '\t')) {
  830.         src++;
  831.     }
  832.     if (*src == '\0') {
  833.         sprintf(interp->result, "missing value on line %d", lineNum);
  834.         return TCL_ERROR;
  835.     }
  836.  
  837.     /*
  838.      * Parse off the value, squeezing out backslash-newline sequences
  839.      * along the way.
  840.      */
  841.  
  842.     dst = value = src;
  843.     while (*src != '\n') {
  844.         if (*src == '\0') {
  845.         sprintf(interp->result, "missing newline on line %d",
  846.             lineNum);
  847.         return TCL_ERROR;
  848.         }
  849.         if ((src[0] == '\\') && (src[1] == '\n')) {
  850.         src += 2;
  851.         lineNum++;
  852.         } else {
  853.         *dst = *src;
  854.         dst++;
  855.         src++;
  856.         }
  857.     }
  858.     *dst = 0;
  859.  
  860.     /*
  861.      * Enter the option into the database.
  862.      */
  863.  
  864.     Tk_AddOption(tkwin, name, value, priority);
  865.     src++;
  866.     lineNum++;
  867.     }
  868.     return TCL_OK;
  869. }
  870.  
  871. /*
  872.  *----------------------------------------------------------------------
  873.  *
  874.  * ReadOptionFile --
  875.  *
  876.  *     Read a file of options ("resources" in the old X terminology)
  877.  *    and load them into the option database.
  878.  *
  879.  * Results:
  880.  *    The return value is a standard Tcl return code.  In the case of
  881.  *    an error in parsing string, TCL_ERROR will be returned and an
  882.  *    error message will be left in interp->result.
  883.  *
  884.  * Side effects:
  885.  *    None.
  886.  *
  887.  *----------------------------------------------------------------------
  888.  */
  889.  
  890. static int
  891. ReadOptionFile(interp, tkwin, fileName, priority)
  892.     Tcl_Interp *interp;        /* Interpreter to use for reporting results. */
  893.     Tk_Window tkwin;        /* Token for window:  options are entered
  894.                  * for this window's main window. */
  895.     char *fileName;        /* Name of file containing options. */
  896.     int priority;        /* Priority level to use for options in
  897.                  * this file, such as TK_USER_DEFAULT_PRIO
  898.                  * or TK_INTERACTIVE_PRIO.  Must be between
  899.                  * 0 and TK_MAX_PRIO. */
  900. {
  901.     char *realName, *buffer;
  902.     int fileId, result;
  903.     struct stat statBuf;
  904.     Tcl_DString newName;
  905.  
  906.     realName = Tcl_TildeSubst(interp, fileName, &newName);
  907.     if (realName == NULL) {
  908.     return TCL_ERROR;
  909.     }
  910.     fileId = open(realName, O_RDONLY, 0);
  911.     Tcl_DStringFree(&newName);
  912.     if (fileId < 0) {
  913.     Tcl_AppendResult(interp, "couldn't read file \"", fileName, "\"",
  914.         (char *) NULL);
  915.     return TCL_ERROR;
  916.     }
  917.     if (fstat(fileId, &statBuf) == -1) {
  918.     Tcl_AppendResult(interp, "couldn't stat file \"", fileName, "\"",
  919.         (char *) NULL);
  920.     close(fileId);
  921.     return TCL_ERROR;
  922.     }
  923.     buffer = (char *) ckalloc((unsigned) statBuf.st_size+1);
  924.     if (read(fileId, buffer, (unsigned) statBuf.st_size) != statBuf.st_size) {
  925.     Tcl_AppendResult(interp, "error reading file \"", fileName, "\"",
  926.         (char *) NULL);
  927.     close(fileId);
  928.     return TCL_ERROR;
  929.     }
  930.     close(fileId);
  931.     buffer[statBuf.st_size] = 0;
  932.     result = AddFromString(interp, tkwin, buffer, priority);
  933.     ckfree(buffer);
  934.     return result;
  935. }
  936.  
  937. /*
  938.  *--------------------------------------------------------------
  939.  *
  940.  * NewArray --
  941.  *
  942.  *    Create a new ElArray structure of a given size.
  943.  *
  944.  * Results:
  945.  *    The return value is a pointer to a properly initialized
  946.  *    element array with "numEls" space.  The array is marked
  947.  *    as having no active elements.
  948.  *
  949.  * Side effects:
  950.  *    Memory is allocated.
  951.  *
  952.  *--------------------------------------------------------------
  953.  */
  954.  
  955. static ElArray *
  956. NewArray(numEls)
  957.     int numEls;            /* How many elements of space to allocate. */
  958. {
  959.     register ElArray *arrayPtr;
  960.  
  961.     arrayPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(numEls));
  962.     arrayPtr->arraySize = numEls;
  963.     arrayPtr->numUsed = 0;
  964.     arrayPtr->nextToUse = arrayPtr->els;
  965.     return arrayPtr;
  966. }
  967.  
  968. /*
  969.  *--------------------------------------------------------------
  970.  *
  971.  * ExtendArray --
  972.  *
  973.  *    Add a new element to an array, extending the array if
  974.  *    necessary.
  975.  *
  976.  * Results:
  977.  *    The return value is a pointer to the new array, which
  978.  *    will be different from arrayPtr if the array got expanded.
  979.  *
  980.  * Side effects:
  981.  *    Memory may be allocated or freed.
  982.  *
  983.  *--------------------------------------------------------------
  984.  */
  985.  
  986. static ElArray *
  987. ExtendArray(arrayPtr, elPtr)
  988.     register ElArray *arrayPtr;        /* Array to be extended. */
  989.     register Element *elPtr;        /* Element to be copied into array. */
  990. {
  991.     /*
  992.      * If the current array has filled up, make it bigger.
  993.      */
  994.  
  995.     if (arrayPtr->numUsed >= arrayPtr->arraySize) {
  996.     register ElArray *newPtr;
  997.  
  998.     newPtr = (ElArray *) ckalloc(EL_ARRAY_SIZE(2*arrayPtr->arraySize));
  999.     newPtr->arraySize = 2*arrayPtr->arraySize;
  1000.     newPtr->numUsed = arrayPtr->numUsed;
  1001.     newPtr->nextToUse = &newPtr->els[newPtr->numUsed];
  1002.     memcpy((VOID *) newPtr->els, (VOID *) arrayPtr->els,
  1003.         (arrayPtr->arraySize*sizeof(Element)));
  1004.     ckfree((char *) arrayPtr);
  1005.     arrayPtr = newPtr;
  1006.     }
  1007.  
  1008.     *arrayPtr->nextToUse = *elPtr;
  1009.     arrayPtr->nextToUse++;
  1010.     arrayPtr->numUsed++;
  1011.     return arrayPtr;
  1012. }
  1013.  
  1014. /*
  1015.  *--------------------------------------------------------------
  1016.  *
  1017.  * SetupStacks --
  1018.  *
  1019.  *    Arrange the stacks so that they cache all the option
  1020.  *    information for a particular window.
  1021.  *
  1022.  * Results:
  1023.  *    None.
  1024.  *
  1025.  * Side effects:
  1026.  *    The stacks are modified to hold information for tkwin
  1027.  *    and all its ancestors in the window hierarchy.
  1028.  *
  1029.  *--------------------------------------------------------------
  1030.  */
  1031.  
  1032. static void
  1033. SetupStacks(winPtr, leaf)
  1034.     TkWindow *winPtr;        /* Window for which information is to
  1035.                  * be cached. */
  1036.     int leaf;            /* Non-zero means this is the leaf
  1037.                  * window being probed.  Zero means this
  1038.                  * is an ancestor of the desired leaf. */
  1039. {
  1040.     int level, i, *iPtr;
  1041.     register StackLevel *levelPtr;
  1042.     register ElArray *arrayPtr;
  1043.  
  1044.     /*
  1045.      * The following array defines the order in which the current
  1046.      * stacks are searched to find matching entries to add to the
  1047.      * stacks.  Given the current priority-based scheme, the order
  1048.      * below is no longer relevant;  all that matters is that an
  1049.      * element is on the list *somewhere*.  The ordering is a relic
  1050.      * of the old days when priorities were determined differently.
  1051.      */
  1052.  
  1053.     static int searchOrder[] = {WILDCARD_NODE_CLASS, WILDCARD_NODE_NAME,
  1054.         EXACT_NODE_CLASS, EXACT_NODE_NAME, -1};
  1055.  
  1056.     if (winPtr->mainPtr->optionRootPtr == NULL) {
  1057.     OptionInit(winPtr->mainPtr);
  1058.     }
  1059.  
  1060.     /*
  1061.      * Step 1:  make sure that options are cached for this window's
  1062.      * parent.
  1063.      */
  1064.  
  1065.     if (winPtr->parentPtr != NULL) {
  1066.     level = winPtr->parentPtr->optionLevel;
  1067.     if ((level == -1) || (cachedWindow == NULL)) {
  1068.         SetupStacks(winPtr->parentPtr, 0);
  1069.         level = winPtr->parentPtr->optionLevel;
  1070.     }
  1071.     level++;
  1072.     } else {
  1073.     level = 1;
  1074.     }
  1075.  
  1076.     /*
  1077.      * Step 2:  pop extra unneeded information off the stacks and
  1078.      * mark those windows as no longer having cached information.
  1079.      */
  1080.  
  1081.     if (curLevel >= level) {
  1082.     while (curLevel >= level) {
  1083.         levels[curLevel].winPtr->optionLevel = -1;
  1084.         curLevel--;
  1085.     }
  1086.     levelPtr = &levels[level];
  1087.     for (i = 0; i < NUM_STACKS; i++) {
  1088.         arrayPtr = stacks[i];
  1089.         arrayPtr->numUsed = levelPtr->bases[i];
  1090.         arrayPtr->nextToUse = &arrayPtr->els[arrayPtr->numUsed];
  1091.     }
  1092.     }
  1093.     curLevel = winPtr->optionLevel = level;
  1094.  
  1095.     /*
  1096.      * Step 3:  if the root database information isn't loaded or
  1097.      * isn't valid, initialize level 0 of the stack from the
  1098.      * database root (this only happens if winPtr is a main window).
  1099.      */
  1100.  
  1101.     if ((curLevel == 1)
  1102.         && ((cachedWindow == NULL)
  1103.         || (cachedWindow->mainPtr != winPtr->mainPtr))) {
  1104.     for (i = 0; i < NUM_STACKS; i++) {
  1105.         arrayPtr = stacks[i];
  1106.         arrayPtr->numUsed = 0;
  1107.         arrayPtr->nextToUse = arrayPtr->els;
  1108.     }
  1109.     ExtendStacks(winPtr->mainPtr->optionRootPtr, 0);
  1110.     }
  1111.  
  1112.     /*
  1113.      * Step 4: create a new stack level;  grow the level array if
  1114.      * we've run out of levels.  Clear the stacks for EXACT_LEAF_NAME
  1115.      * and EXACT_LEAF_CLASS (anything that was there is of no use
  1116.      * any more).
  1117.      */
  1118.  
  1119.     if (curLevel >= numLevels) {
  1120.     StackLevel *newLevels;
  1121.  
  1122.     newLevels = (StackLevel *) ckalloc((unsigned)
  1123.         (numLevels*2*sizeof(StackLevel)));
  1124.     memcpy((VOID *) newLevels, (VOID *) levels,
  1125.         (numLevels*sizeof(StackLevel)));
  1126.     ckfree((char *) levels);
  1127.     numLevels *= 2;
  1128.     levels = newLevels;
  1129.     }
  1130.     levelPtr = &levels[curLevel];
  1131.     levelPtr->winPtr = winPtr;
  1132.     arrayPtr = stacks[EXACT_LEAF_NAME];
  1133.     arrayPtr->numUsed = 0;
  1134.     arrayPtr->nextToUse = arrayPtr->els;
  1135.     arrayPtr = stacks[EXACT_LEAF_CLASS];
  1136.     arrayPtr->numUsed = 0;
  1137.     arrayPtr->nextToUse = arrayPtr->els;
  1138.     levelPtr->bases[EXACT_LEAF_NAME] = stacks[EXACT_LEAF_NAME]->numUsed;
  1139.     levelPtr->bases[EXACT_LEAF_CLASS] = stacks[EXACT_LEAF_CLASS]->numUsed;
  1140.     levelPtr->bases[EXACT_NODE_NAME] = stacks[EXACT_NODE_NAME]->numUsed;
  1141.     levelPtr->bases[EXACT_NODE_CLASS] = stacks[EXACT_NODE_CLASS]->numUsed;
  1142.     levelPtr->bases[WILDCARD_LEAF_NAME] = stacks[WILDCARD_LEAF_NAME]->numUsed;
  1143.     levelPtr->bases[WILDCARD_LEAF_CLASS] = stacks[WILDCARD_LEAF_CLASS]->numUsed;
  1144.     levelPtr->bases[WILDCARD_NODE_NAME] = stacks[WILDCARD_NODE_NAME]->numUsed;
  1145.     levelPtr->bases[WILDCARD_NODE_CLASS] = stacks[WILDCARD_NODE_CLASS]->numUsed;
  1146.  
  1147.  
  1148.     /*
  1149.      * Step 5: scan the current stack level looking for matches to this
  1150.      * window's name or class;  where found, add new information to the
  1151.      * stacks.
  1152.      */
  1153.  
  1154.     for (iPtr = searchOrder; *iPtr != -1; iPtr++) {
  1155.     register Element *elPtr;
  1156.     int count;
  1157.     Tk_Uid id;
  1158.  
  1159.     i = *iPtr;
  1160.     if (i & CLASS) {
  1161.         id = winPtr->classUid;
  1162.     } else {
  1163.         id = winPtr->nameUid;
  1164.     }
  1165.     elPtr = stacks[i]->els;
  1166.     count = levelPtr->bases[i];
  1167.  
  1168.     /*
  1169.      * For wildcard stacks, check all entries;  for non-wildcard
  1170.      * stacks, only check things that matched in the parent.
  1171.      */
  1172.  
  1173.     if (!(i & WILDCARD)) {
  1174.         elPtr += levelPtr[-1].bases[i];
  1175.         count -= levelPtr[-1].bases[i];
  1176.     }
  1177.     for ( ; count > 0; elPtr++, count--) {
  1178.         if (elPtr->nameUid != id) {
  1179.         continue;
  1180.         }
  1181.         ExtendStacks(elPtr->child.arrayPtr, leaf);
  1182.     }
  1183.     }
  1184.     cachedWindow = winPtr;
  1185. }
  1186.  
  1187. /*
  1188.  *--------------------------------------------------------------
  1189.  *
  1190.  * ExtendStacks --
  1191.  *
  1192.  *    Given an element array, copy all the elements from the
  1193.  *    array onto the system stacks (except for irrelevant leaf
  1194.  *    elements).
  1195.  *
  1196.  * Results:
  1197.  *    None.
  1198.  *
  1199.  * Side effects:
  1200.  *    The option stacks are extended.
  1201.  *
  1202.  *--------------------------------------------------------------
  1203.  */
  1204.  
  1205. static void
  1206. ExtendStacks(arrayPtr, leaf)
  1207.     ElArray *arrayPtr;        /* Array of elements to copy onto stacks. */
  1208.     int leaf;            /* If zero, then don't copy exact leaf
  1209.                  * elements. */
  1210. {
  1211.     register int count;
  1212.     register Element *elPtr;
  1213.  
  1214.     for (elPtr = arrayPtr->els, count = arrayPtr->numUsed;
  1215.         count > 0; elPtr++, count--) {
  1216.     if (!(elPtr->flags & (NODE|WILDCARD)) && !leaf) {
  1217.         continue;
  1218.     }
  1219.     stacks[elPtr->flags] = ExtendArray(stacks[elPtr->flags], elPtr);
  1220.     }
  1221. }
  1222.  
  1223. /*
  1224.  *--------------------------------------------------------------
  1225.  *
  1226.  * OptionInit --
  1227.  *
  1228.  *    Initialize data structures for option handling.
  1229.  *
  1230.  * Results:
  1231.  *    None.
  1232.  *
  1233.  * Side effects:
  1234.  *    Option-related data structures get initialized.
  1235.  *
  1236.  *--------------------------------------------------------------
  1237.  */
  1238.  
  1239. static void
  1240. OptionInit(mainPtr)
  1241.     register TkMainInfo *mainPtr;    /* Top-level information about
  1242.                      * window that isn't initialized
  1243.                      * yet. */
  1244. {
  1245.     int i;
  1246.     Tcl_Interp *interp;
  1247.  
  1248.     /*
  1249.      * First, once-only initialization.
  1250.      */
  1251.  
  1252.     if (numLevels == 0) {
  1253.  
  1254.     numLevels = 5;
  1255.     levels = (StackLevel *) ckalloc((unsigned) (5*sizeof(StackLevel)));
  1256.     for (i = 0; i < NUM_STACKS; i++) {
  1257.         stacks[i] = NewArray(10);
  1258.         levels[0].bases[i] = 0;
  1259.     }
  1260.     
  1261.     defaultMatch.nameUid = NULL;
  1262.     defaultMatch.child.valueUid = NULL;
  1263.     defaultMatch.priority = -1;
  1264.     defaultMatch.flags = 0;
  1265.     }
  1266.  
  1267.     /*
  1268.      * Then, per-main-window initialization.  Create and delete dummy
  1269.      * interpreter for message logging.
  1270.      */
  1271.  
  1272.     mainPtr->optionRootPtr = NewArray(20);
  1273.     interp = Tcl_CreateInterp();
  1274.     (void) GetDefaultOptions(interp, mainPtr->winPtr);
  1275.     Tcl_DeleteInterp(interp);
  1276. }
  1277.  
  1278. /*
  1279.  *--------------------------------------------------------------
  1280.  *
  1281.  * ClearOptionTree --
  1282.  *
  1283.  *    This procedure is called to erase everything in a
  1284.  *    hierarchical option database.
  1285.  *
  1286.  * Results:
  1287.  *    None.
  1288.  *
  1289.  * Side effects:
  1290.  *    All the options associated with arrayPtr are deleted,
  1291.  *    along with all option subtrees.  The space pointed to
  1292.  *    by arrayPtr is freed.
  1293.  *
  1294.  *--------------------------------------------------------------
  1295.  */
  1296.  
  1297. static void
  1298. ClearOptionTree(arrayPtr)
  1299.     ElArray *arrayPtr;        /* Array of options;  delete everything
  1300.                  * referred to recursively by this. */
  1301. {
  1302.     register Element *elPtr;
  1303.     int count;
  1304.  
  1305.     for (count = arrayPtr->numUsed, elPtr = arrayPtr->els;  count > 0;
  1306.         count--, elPtr++) {
  1307.     if (elPtr->flags & NODE) {
  1308.         ClearOptionTree(elPtr->child.arrayPtr);
  1309.     }
  1310.     }
  1311.     ckfree((char *) arrayPtr);
  1312. }
  1313.  
  1314. /*
  1315.  *--------------------------------------------------------------
  1316.  *
  1317.  * GetDefaultOptions --
  1318.  *
  1319.  *    This procedure is invoked to load the default set of options
  1320.  *    for a window.
  1321.  *
  1322.  * Results:
  1323.  *    None.
  1324.  *
  1325.  * Side effects:
  1326.  *    Options are added to those for winPtr's main window.  If
  1327.  *    there exists a RESOURCE_MANAGER proprety for winPtr's
  1328.  *    display, that is used.  Otherwise, the .Xdefaults file in
  1329.  *    the user's home directory is used.
  1330.  *
  1331.  *--------------------------------------------------------------
  1332.  */
  1333.  
  1334. static int
  1335. GetDefaultOptions(interp, winPtr)
  1336.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  1337.     TkWindow *winPtr;        /* Fetch option defaults for main window
  1338.                  * associated with this. */
  1339. {
  1340.     char *regProp, *home, *fileName;
  1341.     int result, actualFormat;
  1342.     unsigned long numItems, bytesAfter;
  1343.     Atom actualType;
  1344.  
  1345.     /*
  1346.      * Try the RESOURCE_MANAGER property on the root window first.
  1347.      */
  1348.  
  1349.     regProp = NULL;
  1350.     result = XGetWindowProperty(winPtr->display,
  1351.         RootWindow(winPtr->display, 0),
  1352.         XA_RESOURCE_MANAGER, 0, 100000,
  1353.         False, XA_STRING, &actualType, &actualFormat,
  1354.         &numItems, &bytesAfter, (unsigned char **) ®Prop);
  1355.  
  1356.     if ((result == Success) && (actualType == XA_STRING)
  1357.         && (actualFormat == 8)) {
  1358.     result = AddFromString(interp, (Tk_Window) winPtr, regProp,
  1359.         TK_USER_DEFAULT_PRIO);
  1360.     XFree(regProp);
  1361.     return result;
  1362.     }
  1363.  
  1364.     /*
  1365.      * No luck there.  Try a .Xdefaults file in the user's home
  1366.      * directory.
  1367.      */
  1368.  
  1369.     if (regProp != NULL) {
  1370.     XFree(regProp);
  1371.     }
  1372.     home = getenv("HOME");
  1373.     if (home == NULL) {
  1374.     sprintf(interp->result,
  1375.         "no RESOURCE_MANAGER property and no HOME envariable");
  1376.     return TCL_ERROR;
  1377.     }
  1378.     fileName = (char *) ckalloc((unsigned) (strlen(home) + 20));
  1379.     sprintf(fileName, "%s/.Xdefaults", home);
  1380.     result = ReadOptionFile(interp, (Tk_Window) winPtr, fileName,
  1381.         TK_USER_DEFAULT_PRIO);
  1382.     ckfree(fileName);
  1383.     return result;
  1384. }
  1385.